/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package p1;

import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

/**
 *
 * @author Derek Ward
 * 
 * Loads in a problem file, validates it and 
 * creates Problem objects
 * 
 */
public class ProblemFile 
{
    private     boolean     m_interactiveMode;
    private     Graph       m_graph;
    private     String      m_fileName;
    private     File        m_file;
    
    public ProblemFile(Graph g, String fn)
    {
        if ( g != null )
            m_graph = g;
        else
            throw new NullPointerException("ProblemFile::ProblemFile(Graph, String) was passed a null Graph object (g)!");
        
        if ( fn != null )
            m_fileName = fn;
        else
            throw new NullPointerException("ProblemFile::ProblemFile(Graph, String) was passed a null String object (fn)!");
        
        m_file = new File(m_fileName);
        m_interactiveMode = false;
    }
    
    public ProblemFile(Graph g)
    {
        if ( g != null )
            m_graph = g;
        else
            throw new NullPointerException("ProblemFile::ProblemFile(Graph) was passed a null Graph object (g)!");
        
        m_fileName = null;
        m_file = null;
        m_interactiveMode = true;
    }
    
    public void readProblemFile() throws IOException, InvalidProblemException
    {
        validateFileState();
        
        readData();
    }
    
    private void validateFileState() throws IOException
    {
        if ( !m_interactiveMode )
        {
            if ( m_file == null )
                throw new IOException("ProblemFile::validateFileState() the data file object is null!");

            if ( !m_file.exists() )
            {
                throw new FileNotFoundException("ProblemFile::validateFileState() the file, " + m_fileName + " does not exist!");
            }

            if ( !m_file.canRead() )
            {
                throw new IOException("ProblemFile::DataFilvalidateFileStatee() the file, " + m_fileName + " is not readable!");
            }
        }
    }
    
    private void readData() throws IOException, InvalidProblemException
    {
        Scanner scanner;
        String line;
        
        if ( m_interactiveMode )
        {
            System.out.println("No problem file specified. Interactive mode.");
            System.out.println("Enter in a problem line (end with EOF/Ctrl-D): ");
            scanner = new Scanner(new InputStreamReader(System.in));
        }
        else
        {
            scanner = new Scanner(new FileInputStream(m_file));
        }
        
        while ( scanner.hasNextLine() )
        {
            line = scanner.nextLine();
            parseProblemLine(line).solveProblem(m_graph);
            m_graph.resetNodeValues();
        }
        
        scanner.close();
    }
    
    private Problem parseProblemLine(String s) throws InvalidProblemException
    {
        Problem p = null;
        Scanner scanner;
        String ts;
        int problemType;
        ArrayList <String> cities = new ArrayList<String>();
        
        if ( s != null )
        {
            scanner = new Scanner(s);
        }
        else
            throw new NullPointerException("ProblemFile::parseProblemLine(String) was passed a null String object (s)!");
        
        if ( scanner.hasNextInt() )
        {
            problemType = scanner.nextInt();
            
            switch (problemType)
            {
                case 1:
                    // Expecting two city strings
                    if ( scanner.hasNext() )
                    {
                        ts = scanner.next();
                        
                        if ( m_graph.isValidNode(ts) )
                            cities.add(ts);
                        else
                            throw new InvalidProblemException("ProblemFile::parseProblemLine(String) a city label on this line was not found in the data file (1)!");
                        
                        if ( scanner.hasNext() )
                        {
                            ts = scanner.next();
                        
                            if ( m_graph.isValidNode(ts) )
                                cities.add(ts);
                            else
                                throw new InvalidProblemException("ProblemFile::parseProblemLine(String) a city label on this line was not found in the data file (1)!");

                            if ( scanner.hasNext() )
                            {
                                throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected two city strings on this problem line, but there were more(1)!");
                            }
                            
                            p = new Problem(cities, problemType);
                        }
                        else
                        {
                            throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected two city strings on this problem line (1)!");
                        }
                    }
                    else
                    {
                        throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected two city strings on this problem line (1)!");
                    }
                    break;
                case 2:
                    // Expecting at least three city strings
                    
                    if ( scanner.hasNext() )
                    {
                        ts = scanner.next();
                        
                        if ( m_graph.isValidNode(ts) )
                                cities.add(ts);
                            else
                                throw new InvalidProblemException("ProblemFile::parseProblemLine(String) a city label on this line was not found in the data file (2)!");
                        
                        if ( scanner.hasNext() )
                        {
                            ts = scanner.next();
                        
                            if ( m_graph.isValidNode(ts) )
                                    cities.add(ts);
                                else
                                    throw new InvalidProblemException("ProblemFile::parseProblemLine(String) a city label on this line was not found in the data file (2)!");
                            
                            if ( scanner.hasNext() )
                            {
                                while ( scanner.hasNext() )
                                {
                                    ts = scanner.next();

                                    if ( m_graph.isValidNode(ts) )
                                        cities.add(ts);
                                    else
                                        throw new InvalidProblemException("ProblemFile::parseProblemLine(String) a city label on this line was not found in the data file (2)!");
                                }

                                p = new Problem(cities, problemType);
                            }
                            else
                            {
                                throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected at least three city strings on this problem line, but found only 2 (2)!");
                            }
                            
                        }
                        else
                        {
                            throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected at least three city strings on this problem line, but found only 1 (2)!");
                        }
                    }
                    else
                    {
                        throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected at least three city strings on this problem line, but found none (2)!");
                    }
                    break;
                case 3:
                    // Expecting only one city string
                    if ( scanner.hasNext() )
                    {
                        ts = scanner.next();
                        
                        if ( m_graph.isValidNode(ts) )
                            cities.add(ts);
                        else
                            throw new InvalidProblemException("ProblemFile::parseProblemLine(String) a city label on this line was not found in the data file (1)!");
                        
                        if ( scanner.hasNext() )
                        {
                            throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected only one city string on this problem line, but found too many (3)!");
                        }
                        
                        p = new Problem(cities, problemType);
                    }
                    else
                    {
                        throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected only one city string on this problem line, but found none (3)!");
                    }
                    break;
                default:
                    throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected a number within the proper range for this problem (1 - 3)!");
            }
        }
        else
        {
            throw new InvalidProblemException("ProblemFile::parseProblemLine(String) expected a number at the beginning of a problem line!");
        }
        
        scanner.close();
        
        return p;
    }
}
